#include "libPPD.h"
#include "N-Step-SCAN.h"

void inicializarVariables()
{
	extern t_log* loguear;
	extern t_config config;
	extern tCabezal cabezal;
	extern pthread_mutex_t bloqueaLista;
	extern pthread_mutex_t bloqueaNLista;
	extern sem_t semaforoLista;
	extern sem_t sublistaLlena;
	extern sem_t listaPedidosVacia;
	extern int32_t fileDescriptor;
	extern int32_t contadorEscriturasSectores;
	extern int32_t cantidadMSync;

	/* Creo el Log solo para Archivo sin Consola*/
		loguear = log_create("PPD", "log.txt", ERROR | INFO | WARNING, M_CONSOLE_DISABLE);



	/* Leo Archivo Config */
		config = abrirArchivoConfig();
		log_info(loguear, "PPD", "Message Info: %s", "Se abrio el Archivo de Configuracion");



	/* Mapeo el Disco */
		fileDescriptor = 0;
		fileDescriptor = abrirMapearArchivoDisco ();
		if (fileDescriptor == Retorno_Error)
			log_error (loguear,  "PPD", "Message Error: %s", "Error al mapear el Archivo Disco");


	/* Inicializo el cabezal y contadores */
		pasarACHS(0, &cabezal.posActual);
		cabezal.sentido = ASCENDENTE;
		contadorEscriturasSectores = 0;
		cantidadMSync = 0;


	/* Creo mutex y semaforo de la Lista de Pedidos*/
		pthread_mutex_init (&bloqueaLista, NULL);
		pthread_mutex_init (&bloqueaNLista, NULL);
		int32_t valorRetorno;
		valorRetorno = sem_init(&semaforoLista, 0, 0);


	/* Creo las listas  de N Step Scan y el hilo para sincronizarlas */
		if (strcmp(config.algoritmo, "NSTEPSCAN") == 0)
		{
			extern tCabezal cabezalAux;
			extern int32_t primerNodoLista;
			primerNodoLista = 0;
			valorRetorno = sem_init (&sublistaLlena, 0, 0);
			valorRetorno = sem_init (&listaPedidosVacia, 0, 0);
			if ((strcmp(config.modoInicio, "CONNECT") == 0))
				crearNListaPRAID ();
			else if (strcmp(config.modoInicio, "LISTEN") == 0)
				crearNListaPFS ();
			pthread_t hiloPlanificacion;
			pthread_create (&hiloPlanificacion, NULL, (void *) sincronizarListas, NULL);
			cabezalAux = cabezal;
		}
}


void procesoConsola()
{
	extern t_log *loguear;
	extern t_config config;
	extern int32_t sockConsola;
	int32_t listenSockConsola;

	listenSockConsola = AFUNIX_abrir_conexion("/tmp/sock");
	if(listenSockConsola < 0)
		printf ("No se pudo abrir el servidor para la Consola\n");
	iniciarConsola();
	sockConsola = aceptar_conexion (listenSockConsola);

}

int32_t iniciarConsola ()
{
	extern t_config config;
	pid_t pidConsola;
	char *rutaProceso[2];

	rutaProceso[0] = config.pathConsola;
	rutaProceso [1] = NULL;

	pidConsola = fork();
	if (pidConsola == -1)
		printf("Error al crear el Proceso Consola\n");
	else if (pidConsola == 0)
		if (execv (rutaProceso[0], rutaProceso))
			printf ("Error al levantar el Proceso Consola\n");

	return Retorno_OK;
}

void cerrarConsola(int32_t signal)
{
	while (waitpid(-1, 0, WNOHANG));
}


uint32_t calcularDireccion(uint32_t unSector)
{
	uint32_t iDirARetornar;
	iDirARetornar = unSector * TAMSECTOR;

	return iDirARetornar;
}



int32_t abrirMapearArchivoDisco ()
{
	extern t_config config;
	extern char *archivoMapeado;
	int32_t fileDescriptor;
    int32_t statusOk =0;
    struct stat stStatusArchivo;

	if((fileDescriptor = open (config.nombreVD,O_RDWR)) <= 0)
    {
        printf ("Error al abrir el archivo");
		return Retorno_Error;
    }

	statusOk = fstat (fileDescriptor, &stStatusArchivo);
	archivoMapeado = NULL;

    archivoMapeado = mmap((caddr_t)0, stStatusArchivo.st_size, PROT_WRITE, MAP_SHARED, fileDescriptor,(off_t)0);
    if (archivoMapeado == (caddr_t)-1)
    {
    	perror("mmap() error");

        if (posix_madvise(archivoMapeado, stStatusArchivo.st_size, POSIX_MADV_RANDOM) != 0)
        	perror("madvise() error");

    	return Retorno_Error;
    }

    return fileDescriptor;
}

int32_t cerrarArchivoMapeado()
{
	extern int32_t fileDescriptor;
	extern char *archivoMapeado;
	struct stat stStatusArchivoCerrar;
	int32_t statusOk = 0;
	statusOk = fstat(fileDescriptor, &stStatusArchivoCerrar);

	if((munmap(archivoMapeado,stStatusArchivoCerrar.st_size)) != Retorno_OK)
    	{
    		perror("Error Al Hacer unmap\n");
    		return Retorno_Error;
    	}

	 if ((close(fileDescriptor))!= Retorno_OK)
	    {
	    	printf("Error En el Close\n");
	    	return Retorno_Error;
	    }

	 archivoMapeado = NULL;
	 return Retorno_OK;
}


int32_t handshakePRAID ()
{
	extern t_config config;
	extern t_log *loguear;
	extern int32_t socketPRAID;
	t_NIPC paquete, respuesta;
	int32_t tamMensaje = 0;
	int32_t valorRetorno;
	char *mensaje = NULL;
	tamMensaje = 4*(sizeof (int32_t) +1);
	mensaje = (char*) calloc (4, tamMensaje);
	sprintf(mensaje, "%d %d %d %d", config.idDisco, config.c, config.h, config.s);

	NIPC_crearpaquete(NULL,HANDSHAKE,tamMensaje,mensaje,0, &paquete);

	valorRetorno = NIPC_enviar(socketPRAID, paquete);

	valorRetorno = NIPC_recibir(socketPRAID, &respuesta);

	free(mensaje);
	NIPC_destruirpaquete(&paquete);
//	NIPC_destruirpaquete(&respuesta);

	return Retorno_OK;
}

int32_t handshakePFS (int32_t socketPFS)
{
	t_NIPC paqueteRecibido, respuesta;
	int32_t valorRetorno;

	valorRetorno = NIPC_recibir (socketPFS, &paqueteRecibido);
	if (paqueteRecibido.PayloadDescriptor == HANDSHAKE)
	{
		NIPC_crearpaquete(NULL, HANDSHAKE, 0, NULL, 0, &respuesta);
		valorRetorno = NIPC_enviar(socketPFS,respuesta);
		NIPC_destruirpaquete(&respuesta);
	}

	NIPC_destruirpaquete(&paqueteRecibido);

	if (valorRetorno < 0)
		return -1;
	else
		return Retorno_OK;
}


int32_t leerSector(uint32_t iNumeroSector,void *vDatosAretornar)
{
      extern char *archivoMapeado;
      uint32_t iDirALeer;
      iDirALeer = calcularDireccion(iNumeroSector);

      memcpy(vDatosAretornar,archivoMapeado + iDirALeer, TAMSECTOR);

      return Retorno_OK;

}

int32_t escribirSector(uint32_t iNumeroSector,char *vDatosAEscribir)
{
    uint32_t iDirAEscribir;
    extern char *archivoMapeado;
    extern int32_t cantidadMSync;
    extern int32_t fileDescriptor;
    extern t_config config;
    struct stat stStatusArchivoCerrar;
    iDirAEscribir = calcularDireccion(iNumeroSector);
    int32_t statusOk = 0;
	statusOk = fstat(fileDescriptor, &stStatusArchivoCerrar);

    memcpy(archivoMapeado + iDirAEscribir, vDatosAEscribir,TAMSECTOR);
    cantidadMSync++;
    if(cantidadMSync == config.cantMSync)
    {
    	cantidadMSync = 0;
    	msync(archivoMapeado, stStatusArchivoCerrar.st_size, MS_ASYNC);
    }

    return Retorno_OK;

}
